using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Data;
using visAd.ExceptionHandler;

namespace visAd.ServerDataAddIn
{
    public class XMLHelper
    {
        private bool IsValidFilterXML = false;
        private const string mClassName = "XMLHelper";

        #region "Visio AddIn XML Generation"
        
        public XmlDocument CreateXmlDocument(DataSet dtFilterData, bool IsFinal)
        {
            string ServerType;

            XmlDocument filterXML = CreateXmlDocument();

            filterXML = CreateXmlDocument("visAd");

            DataTable dtServerTypes = dtFilterData.Tables["va-servertype"];
            DataTable dtServerNames = dtFilterData.Tables["va-servers"];
            DataTable dtFieldNames = dtFilterData.Tables["va-field"];
            DataTable dtFieldAttributes = dtFilterData.Tables["va-filterattribute"];
            bool IsSelected = true;

            for (int i = 0; i < dtServerTypes.Rows.Count; i++)
            {
                if (IsFinal)
                  IsSelected = (bool)dtServerTypes.Rows[i]["IsSelected"];
                
                if (IsSelected)
                {
                    ServerType = dtServerTypes.Rows[i][0].ToString();

                    Insert(filterXML, "/va-servertype", "type", ServerType);

                    if (!IsFinal)
                    {
                        XmlNode xnode = filterXML.SelectSingleNode("/visAd/va-servertype[@type='" + ServerType + "']");
                        CreateAttribute(xnode, "IsSelected", dtServerTypes.Rows[i]["IsSelected"].ToString());
                    }
                    //Insert(filterXML, "/va-servertype[@type='" + ServerType + "']", "IsSelected", dtServerTypes.Rows[i]["IsSelected"].ToString());
                    Insert(filterXML, "/va-servertype[@type='" + ServerType + "']/va-servers", FilterServerTypeData(dtServerNames, ServerType,IsFinal));
                    Insert(filterXML, "/va-servertype[@type='" + ServerType + "']/va-filter");

                    DataTable dtFilterFields = FilterServerTypeData(dtFieldNames, ServerType,IsFinal);
                    Insert(filterXML, "/va-servertype[@type='" + ServerType + "']/va-filter/va-field", dtFilterFields);

                    DataTable dtFilterFieldAttributes = FilterServerTypeData(dtFieldAttributes, ServerType, IsFinal);

                    for (int rowCount = 0; rowCount < dtFilterFields.Rows.Count; rowCount++)
                    {
                        string FieldName = dtFilterFields.Rows[rowCount][0].ToString();

                        for (int attrcount = 0; attrcount < dtFilterFieldAttributes.Rows.Count; attrcount++)
                        {
                            if (dtFilterFieldAttributes.Rows[attrcount]["field"].ToString() == FieldName)
                            {
                                Insert(filterXML, "/va-servertype[@type='" + ServerType + "']/va-filter/va-field[@fieldname='" + FieldName + "']/va-filterattribute", "filtername", dtFilterFieldAttributes.Rows[attrcount]["filtername"].ToString());
                            }
                        }
                    }
                }
            }
            return (filterXML);
        }
       /* public XmlDocument CreateXmlDocumentForCriteria(DataSet dtFilterData, string serverType)
        {


            XmlDocument filterXML = CreateXmlDocument();

            filterXML = CreateXmlDocument("visAd");

            DataTable dtServerTypes = dtFilterData.Tables["va-servertype"];
            DataTable dtServerNames = dtFilterData.Tables["va-servers"];
            DataTable dtFieldNames = dtFilterData.Tables["va-field"];
            DataTable dtFieldAttributes = dtFilterData.Tables["va-filterattribute"];
            bool IsSelected = true;



            if (dtServerTypes.Rows[i][0] == serverType)
            {
                IsSelected = true;
            }
            else
            {
                IsSelected = false;
            }
            if (IsSelected)
            {

                Insert(filterXML, "/va-servertype", "type", serverType);

                if (!IsFinal)
                {
                    XmlNode xnode = filterXML.SelectSingleNode("/visAd/va-servertype[@type='" + serverType + "']");
                    CreateAttribute(xnode, "IsSelected", dtServerTypes.Rows[i]["IsSelected"].ToString());
                }
                //Insert(filterXML, "/va-servertype[@type='" + ServerType + "']", "IsSelected", dtServerTypes.Rows[i]["IsSelected"].ToString());
                Insert(filterXML, "/va-servertype[@type='" + serverType + "']/va-servers", FilterServerTypeData(dtServerNames, serverType, IsFinal));
                Insert(filterXML, "/va-servertype[@type='" + serverType + "']/va-filter");

                DataTable dtFilterFields = FilterServerTypeData(dtFieldNames, serverType, IsFinal);
                Insert(filterXML, "/va-servertype[@type='" + serverType + "']/va-filter/va-field", dtFilterFields);

                DataTable dtFilterFieldAttributes = FilterServerTypeData(dtFieldAttributes, serverType, IsFinal);

                for (int rowCount = 0; rowCount < dtFilterFields.Rows.Count; rowCount++)
                {
                    string FieldName = dtFilterFields.Rows[rowCount][0].ToString();

                    for (int attrcount = 0; attrcount < dtFilterFieldAttributes.Rows.Count; attrcount++)
                    {
                        if (dtFilterFieldAttributes.Rows[attrcount]["field"].ToString() == FieldName)
                        {
                            Insert(filterXML, "/va-servertype[@type='" + serverType + "']/va-filter/va-field[@fieldname='" + FieldName + "']/va-filterattribute", "filtername", dtFilterFieldAttributes.Rows[attrcount]["filtername"].ToString());
                        }
                    }
                }

            }
            return (filterXML);

        }*/
        private DataTable FilterServerTypeData(DataTable dtData, string serverType, bool IsFinal)
        {
            string tableName = string.Empty;
            DataRow[] drFilterData;
            DataTable dtServerTypeBasedData;

            tableName = dtData.TableName;

            DataTable dtFilterData = dtData.Copy();

            if (IsFinal)
            {
                drFilterData = dtFilterData.Select("ServerType ='" + serverType + "' and IsSelected=true");
                dtServerTypeBasedData = new DataTable(tableName);

                for (int columnCount = 0; columnCount < dtFilterData.Columns.Count; columnCount++)
                {
                    if (dtFilterData.Columns[columnCount].ColumnName != "IsSelected" & dtFilterData.Columns[columnCount].ColumnName != "ServerType")
                        dtServerTypeBasedData.Columns.Add(dtFilterData.Columns[columnCount].ColumnName);
                }

            }
            else
            {
                drFilterData = dtFilterData.Select("ServerType ='" + serverType + "'");
                dtServerTypeBasedData = new DataTable(tableName);

                for (int columnCount = 0; columnCount < dtFilterData.Columns.Count; columnCount++)
                {
                        dtServerTypeBasedData.Columns.Add(dtFilterData.Columns[columnCount].ColumnName);
                }
            }

            int count = dtServerTypeBasedData.Columns.Count;

            for (int rowCount = 0; rowCount < drFilterData.Length; rowCount++)
            {
                DataRow drNew = dtServerTypeBasedData.NewRow();

                for (int colCount = 0; colCount < dtServerTypeBasedData.Columns.Count; colCount++)
                {
                    drNew[dtServerTypeBasedData.Columns[colCount].ColumnName] = drFilterData[rowCount][dtServerTypeBasedData.Columns[colCount].ColumnName];
                }
                dtServerTypeBasedData.Rows.Add(drNew);
            }
            return (dtServerTypeBasedData);
        }

        public XmlDocument GetFilterFromPersistedXML(XmlDocument serverList)
        {
            try
            {
                XmlNodeList nodeList;
                bool IsAddNewColumn = true;

                //populate server types
                nodeList = serverList.SelectNodes("/visAd/va-servertype");
                DataTable dtServerTypes = new DataTable("va-servertype");
                DataTable dtServerNames = new DataTable("va-servers");
                DataTable dtFieldNames = new DataTable("va-field");
                DataTable dtFieldAttributes = new DataTable("va-filterattribute");

                dtServerTypes = GetDataTable(nodeList, dtServerTypes, IsAddNewColumn);

                for (int ServerTypeCount = 0; ServerTypeCount < dtServerTypes.Rows.Count; ServerTypeCount++)
                {
                    string ServerType = dtServerTypes.Rows[ServerTypeCount][0].ToString();

                    //populate all the server ids
                    nodeList = serverList.SelectNodes("/visAd/va-servertype[@type='" + ServerType + "']/va-servers");
                    dtServerNames = GetDataTable(nodeList, dtServerNames, IsAddNewColumn);

                    nodeList = serverList.SelectNodes("/visAd/va-servertype[@type='" + ServerType + "']/va-filter/va-field");
                    dtFieldNames = GetDataTable(nodeList, dtFieldNames, IsAddNewColumn);

                    for (int rowCount = 0; rowCount < dtFieldNames.Rows.Count; rowCount++)
                    {
                        string FieldName = dtFieldNames.Rows[rowCount][0].ToString();

                        if (ServerType == dtFieldNames.Rows[rowCount]["ServerType"].ToString())
                        {
                            nodeList = serverList.SelectNodes("/visAd/va-servertype[@type='" + ServerType + "']/va-filter/va-field[@fieldname='" + FieldName + "']/va-filterattribute");
                            dtFieldAttributes = GetDataTable(nodeList, dtFieldAttributes, ServerType, IsAddNewColumn);
                            dtFieldAttributes = UpdateFieldAttributes(dtFieldAttributes, FieldName, ServerType, IsAddNewColumn);
                        }
                        IsAddNewColumn = false;
                    }

                    IsAddNewColumn = false;
                }

                DataSet dsFinalFilter = new DataSet("visAd");
                dsFinalFilter.Tables.Add(dtServerTypes.Copy());

                dsFinalFilter.Tables.Add(dtServerNames.Copy());
                dsFinalFilter.Tables.Add(dtFieldNames.Copy());
                dsFinalFilter.Tables.Add(dtFieldAttributes.Copy());

                XmlDocument filterxml = CreateXmlDocument(dsFinalFilter, true);

                return (filterxml);
            }
            catch (vaCustomException vaEx)
            {
                vaEx.AddTrace(mClassName, "RePopulateFilterData");
                throw vaEx;
            }
            catch (Exception ex)
            {
                throw new vaCustomException(ex, mClassName, "RePopulateFilterData");
            }
        }

        private DataTable GetDataTable(XmlNodeList nodelist, DataTable dtData, bool isAddNewColumn)
        {
            try
            {
                XmlNode node = null;

                if (nodelist == null)
                    return null;

                int nodeCount = nodelist.Count;

                // get parameter names
                node = nodelist.Item(0);
                if (node == null)
                    return null;

                XmlAttributeCollection attrCollection = node.Attributes;
                if (attrCollection == null)
                    return null;
                if (attrCollection.Count == 0)
                    return null;

                if (isAddNewColumn)
                {
                    foreach (XmlAttribute attrName in attrCollection)
                    {
                        if (attrName.Name == "IsSelected")
                        {
                            dtData.Columns.Add(attrName.Name, System.Type.GetType("System.Boolean"));
                        }
                        else
                        {
                            dtData.Columns.Add(attrName.Name, System.Type.GetType("System.String"));
                        }
                    }

                    if (!dtData.Columns.Contains("IsSelected"))
                    {
                        dtData.Columns.Add("IsSelected", System.Type.GetType("System.Boolean"));
                    }
                    if (!dtData.Columns.Contains("ServerType"))
                    {
                        dtData.Columns.Add("ServerType", System.Type.GetType("System.String"));
                        // IsServerTypeExists = false;
                    }
                }

                // add rows
                DataRow drAttr = null;
                foreach (XmlNode n in nodelist)
                {
                    drAttr = dtData.NewRow();
                    foreach (XmlAttribute attrVal in n.Attributes)
                    {
                        drAttr[attrVal.Name] = attrVal.Value;
                    }
                    dtData.Rows.Add(drAttr);
                }

                dtData.AcceptChanges();
                return dtData;
            }
            catch (vaCustomException vaEx)
            {
                vaEx.AddTrace(mClassName, "GetDataTable");
                throw vaEx;
            }
            catch (Exception ex)
            {
                throw new vaCustomException(ex, mClassName, "GetDataTable");
            }
        }

        private DataTable GetDataTable(XmlNodeList nodelist, DataTable dtData, string serverType, bool isAddNewColumn)
        {
            try
            {
                XmlNode node = null;

                if (nodelist == null)
                    return null;

                int nodeCount = nodelist.Count;

                // get parameter names
                node = nodelist.Item(0);
                if (node == null)
                    return null;

                XmlAttributeCollection attrCollection = node.Attributes;
                if (attrCollection == null)
                    return null;
                if (attrCollection.Count == 0)
                    return null;

                if (isAddNewColumn)
                {
                    foreach (XmlAttribute attrName in attrCollection)
                    {
                        dtData.Columns.Add(attrName.Name);
                    }
                    dtData.Columns.Add("IsSelected", System.Type.GetType("System.Boolean"));
                    dtData.Columns.Add("ServerType", System.Type.GetType("System.String"));
                }

                // add rows
                DataRow drAttr = null;
                foreach (XmlNode n in nodelist)
                {
                    drAttr = dtData.NewRow();
                    foreach (XmlAttribute attrVal in n.Attributes)
                    {
                        drAttr[attrVal.Name] = attrVal.Value;
                        drAttr["IsSelected"] = false;
                        drAttr["ServerType"] = serverType;
                    }
                    dtData.Rows.Add(drAttr);
                }

                dtData.AcceptChanges();
                return dtData;
            }
            catch (vaCustomException vaEx)
            {
                vaEx.AddTrace(mClassName, "GetDataTable");
                throw vaEx;
            }
            catch (Exception ex)
            {
                throw new vaCustomException(ex, mClassName, "GetDataTable");
            }
        }

        private DataTable UpdateFieldAttributes(DataTable dtFieldAttributes, string fieldName, string serverType, bool IsAddColumn)
        {
            try
            {
                if (IsAddColumn)
                {
                    dtFieldAttributes.Columns.Add("Field", System.Type.GetType("System.String"));
                }

                for (int rowCount = 0; rowCount < dtFieldAttributes.Rows.Count; rowCount++)
                {
                    if (dtFieldAttributes.Rows[rowCount]["Field"].ToString() == string.Empty)
                    {
                        dtFieldAttributes.Rows[rowCount]["Field"] = fieldName;
                    }
                    dtFieldAttributes.Rows[rowCount]["IsSelected"] = true;
                }
                return (dtFieldAttributes);
            }
            catch (vaCustomException vaEx)
            {
                vaEx.AddTrace(mClassName, "UpdateFieldAttributes");
                throw vaEx;
            }
            catch (Exception ex)
            {
                throw new vaCustomException(ex, mClassName, "UpdateFieldAttributes");
            }
        }
        #endregion

        #region XmlDocument Creation

        public static XmlDocument CreateXmlDocument()
        {
            XmlDocument doc = new XmlDocument();
            XmlDeclaration decl = doc.CreateXmlDeclaration("1.0", "utf-8", "");
            doc.InsertBefore(decl, doc.DocumentElement);
            return doc;
        }

        public static XmlDocument CreateXmlDocument(string rootName)
        {
            XmlDocument doc = new XmlDocument();
            XmlDeclaration decl = doc.CreateXmlDeclaration("1.0", "utf-8", "");
            doc.InsertBefore(decl, doc.DocumentElement);
            XmlNode newNode = doc.CreateElement(rootName);
            doc.AppendChild(newNode);
            return doc;
        }

        public static XmlNode Insert(XmlDocument doc, string xpath)
        {
            VerifyParameters(doc, xpath);

            string path2 = xpath.Trim('/');   // get rid of slashes in front and back
            string[] segments = path2.Split('/');

            XmlNode firstNode = doc.LastChild;
            int nodeIndex = 0;

            if (segments.Length > 1)
            {
                ///
                /// Check if we can access the last node.  This comes in handy in cases when the path
                /// contains attributes.  For example: "University[@Name='UT']/Student[@Id=12222]/Address"
                /// In example above we would want to get access to last node directly
                ///
                int pos = path2.LastIndexOf('/');
                string path3 = path2.Substring(0, pos);
                XmlNode parentNode = doc.LastChild.SelectSingleNode(path3);
                if (parentNode != null)
                {
                    firstNode = parentNode;
                    nodeIndex = segments.Length - 1;
                }
            }

            XmlNode lastNode = InsertNode(firstNode, segments, nodeIndex);
            return lastNode;
        }

        /// <summary>
        /// Inserts a record with a single field at the bottom of the hierarchy.
        /// </summary>
        /// <param name="xpath">The xml XPath query to get to the bottom node.</param>
        /// <param name="field">The field to add to the record.</param>
        /// <param name="val">The value assigned to the field.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when an argument is null.</exception>
        /// <remarks>
        /// The "doc" variable must have a root node.  The path should not contain the root node.
        /// The path can contain only the node names or it can contain attributes in XPath query form.
        /// For example to insert an "Address" node at the bottom, the following is a valid xpath query
        ///     xpath = "University[@Name='UT']/Student[@Id=12222]/Address"
        /// </remarks>
        public static void Insert(XmlDocument doc, string xpath, string field, string val)
        {
            VerifyParameters(doc, xpath);
            if (field == null)
            {
                throw (new ArgumentNullException("field cannot be null."));
            }
            if (val == null)
            {
                throw (new ArgumentNullException("val cannot be null."));
            }

            XmlNode node = Insert(doc, xpath);
            CreateAttribute(node, field, val);
        }

        protected static void VerifyParameters(XmlDocument doc, string xpath)
        {
            if (doc == null)
            {
                throw new Exception("doc cannot be null.");
            }
            if (doc.LastChild.GetType() == typeof(System.Xml.XmlDeclaration))
            {
                throw new Exception("XmlDocument requires at least the a root node");
            }
            if (xpath == null)
            {
                throw (new ArgumentNullException("path cannot be null."));
            }
        }

        public static void Insert(XmlDocument doc, string xpath, DataRow rowValues)
        {
            VerifyParameters(doc, xpath);
            if (rowValues == null)
            {
                throw (new ArgumentNullException("rowValues cannot be null."));
            }

            XmlNode node = Insert(doc, xpath);
            foreach (DataColumn col in rowValues.Table.Columns)
            {
                CreateAttribute(node, col.ColumnName, rowValues[col.ColumnName].ToString());
            }
        }

        /// <summary>
        /// Inserts a record with multiple fields from a DataTable at bottom of the hierarchy.
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="xpath">The xml XPath query to get to the bottom node.</param>
        /// <param name="rowValues">The DataRow values that will be added as attributes.</param>
        public static void Insert(XmlDocument doc, string xpath, DataTable table)
        {
            VerifyParameters(doc, xpath);
            if (table == null)
            {
                throw (new ArgumentNullException("table cannot be null."));
            }

            foreach (DataRow row in table.Rows)
            {
                Insert(doc, xpath, row);
            }
        }

        public static XmlAttribute CreateAttribute(XmlNode node, string attributeName, string value)
        {
            XmlDocument doc = node.OwnerDocument;
            XmlAttribute attr = null;
            // create new attribute
            attr = doc.CreateAttribute(attributeName);
            attr.Value = value;
            // link attribute to node
            node.Attributes.SetNamedItem(attr);
            return attr;
        }

        /// <summary>
        /// Inserts a node at the specified segment if it doesn't exist, otherwise
        /// traverses the node.
        /// </summary>
        /// <param name="node">The current node.</param>
        /// <param name="segments">The path segment list.</param>
        /// <param name="idx">The current segment.</param>
        /// <returns></returns>
        protected static XmlNode InsertNode(XmlNode node, string[] segments, int idx)
        {
            XmlNode newNode = null;

            if (idx == segments.Length)
            {
                // All done.
                return node;
            }

            // Traverse the existing hierarchy but ensure that we create a 
            // new record at the last leaf.
            if (idx + 1 < segments.Length)
            {
                foreach (XmlNode child in node.ChildNodes)
                {
                    if (child.Name == segments[idx])
                    {
                        newNode = InsertNode(child, segments, idx + 1);
                        return newNode;
                    }
                }
            }
            XmlDocument doc = node.OwnerDocument;
            newNode = doc.CreateElement(segments[idx]);
            node.AppendChild(newNode);
            XmlNode nextNode = InsertNode(newNode, segments, idx + 1);
            return nextNode;
        }

        #endregion

        public bool ValidateXml(string xml)
        {
          //  bool validXml = false;
            IsValidFilterXML = true;
            //Load the XML data into memory
            

            string xmlpath = AppDomain.CurrentDomain.BaseDirectory + @"..\..\..\visAd.Framework\ServerList.xml";
            XmlReader xmlReader = XmlReader.Create(xmlpath);

            //XmlValidatingReader valReader = new XmlValidatingReader(xmlReader, XmlNodeType.Document, null);
            XmlValidatingReader valReader = new XmlValidatingReader(xmlReader);

            string schemapath = AppDomain.CurrentDomain.BaseDirectory + @"..\..\..\visAd.Framework\ServerFilterSchema.xsd";

            valReader.Schemas.Add(null, schemapath);
            valReader.ValidationType = ValidationType.Schema;
            valReader.ValidationEventHandler += new System.Xml.Schema.ValidationEventHandler(valReader_ValidationEventHandler);
            //valReader.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);
            //Loop through the XML file 
            while (valReader.Read())
            { }
            //if (IsValidFilterXML)
            //    validXml = false;
            //else
            //    validXml = true;
            valReader.Close();
            return IsValidFilterXML;
        }

        void valReader_ValidationEventHandler(object sender, System.Xml.Schema.ValidationEventArgs e)
        {
            IsValidFilterXML = false;
        }



    }
}
